//
// GPSMapEdit
// (c) Konstantin Galichsky (kg@geopainting.com), 2002-2007
//
// Change label attribute for object.
//

# include "StdAfx.h"
# include "UndoRedo.h"
# include "Map.h"
# include "Rename.h"
# include "Label.h"
# include "MapView.h"
# include "Position.h"
# include "Selection.h"
# include "Types.h"
# include "Globals.h"

class CRenameAction : public CEditAction {
  public:
	CRenameAction (rgn_t & _rgn, const string_t & _strNewLabel) :
		m_rgn (_rgn),
		m_strNewLabel (_strNewLabel),
		m_strPrevLabel (_rgn.strLabel),
		m_bWasModified ((_rgn.dwFlags & rgn_t::fModified) != 0)
	{}

	virtual void GetName (string_t & _str) const;
	virtual cu_t CanUndo () const {return cuYes;}
	virtual bool Do ();
	virtual void Undo ();
	virtual void Redo ();

  private:
	rgn_t &  m_rgn;
	string_t const m_strNewLabel;
	string_t const m_strPrevLabel;
	bool     const m_bWasModified;
};

void CRenameAction::GetName (string_t & _str) const {
	CCritSec::CLock lock (g_cs);

	_str.append ("rename [");
	GetHintText (m_rgn, _str);
	_str.append ("]");
}

bool CRenameAction::Do () {
	CCritSec::CLock lock (g_cs);

	// Update label' text.
	m_rgn.strLabel = m_strNewLabel;

	// Update label attributes.
	const type_desc_t * const pTD = m_rgn.GetTypeDesc ();
	assert (pTD);
	if (pTD)
		pTD->pfPrepareLabel (m_rgn);

	m_rgn.dwFlags |= rgn_t::fModified;

	return true;
}

void CRenameAction::Undo () {
	CCritSec::CLock lock (g_cs);

	// Revert label' text
	m_rgn.strLabel = m_strPrevLabel;

	// Update label attributes.
	const type_desc_t * const pTD = m_rgn.GetTypeDesc ();
	assert (pTD);
	if (pTD)
		pTD->pfPrepareLabel (m_rgn);

	if (! m_bWasModified)
		m_rgn.dwFlags &= ~rgn_t::fModified;
}

void CRenameAction::Redo () {
	CRenameAction::Do ();
}

/////////////////////////////////////////////////////////////////

void RenameObject (rgn_t & _rgn, const string_t & _strLabel) {
	PerformEditAction (new CRenameAction (_rgn, _strLabel));
}

/////////////////////////////////////////////////////////////////

static HWND g_hLabelEditWnd;
static WNDPROC g_wpLabelEdit = NULL;
LRESULT APIENTRY _LabelEditProc (HWND _hWnd, UINT _uMsg, WPARAM _wParam, LPARAM _lParam);

bool InitLabelEdit () {
	assert (g_hMapWnd);

	g_hLabelEditWnd = ::CreateWindow (
		"EDIT", NULL,
		ES_LEFT | ES_AUTOHSCROLL | WS_CHILDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_BORDER,
		0, 0, 1, 1,
		g_hMapWnd, NULL, g_hInst, NULL
	);
	if (! g_hLabelEditWnd)
		return false;

	// Subclass the edit box to hook ENTER and ESC.
	g_wpLabelEdit = reinterpret_cast<WNDPROC> (::GetWindowLong (g_hLabelEditWnd, GWL_WNDPROC));
	::SetWindowLong (g_hLabelEditWnd, GWL_WNDPROC, reinterpret_cast<long> (_LabelEditProc));

	return true;
}

void OnEditLabelStart () {
	if (g_Selection.size () != 1 || ! g_Selection [0].pLayer)
		return;

	rgn_t & rgn = * g_Selection [0].iRgn;

	if (! (rgn.dwFlags & rgn_t::fPtLabelDefined)) {
		const type_desc_t * const pTD = rgn.GetTypeDesc ();
		assert (pTD);
		if (pTD)
			pTD->pfPrepareLabel (rgn);
	}

	if (rgn.dwFlags & rgn_t::fPtLabelDefined) {
		g_bDialogMode = true;

		// Set font.
		const type_desc_t * const pTD = rgn.GetTypeDesc ();
		const HFONT hFont = pTD && pTD->phFont ? * (pTD->phFont) : g_hFont;
		::SendMessage (g_hLabelEditWnd, WM_SETFONT, reinterpret_cast<WPARAM> (hFont), FALSE);

		// Set text.
		::SetWindowText (g_hLabelEditWnd, rgn.strLabel.c_str ());

		// Set position & size.
		int w = rgn.ucLabelWidthBy8*8UL + 20;
		if (w < 50)
			w = 50;
		if (w >= 200)
			w = 200;
		const int h = rgn.ucLabelHeight ? rgn.ucLabelHeight + 2 : 14;
		int x = XToScreen (rgn.ptLabel.x);
		if (x + w > g_lWidth)
			x = g_lWidth - w;
		if (x < 0)
			x = 0;
		int y = YToScreen (rgn.ptLabel.y);
		if (y + h > g_lHeight)
			y = g_lHeight - h;
		if (y < 0)
			y = 0;
		::SetWindowPos (g_hLabelEditWnd, NULL, x, y, w, h, SWP_NOZORDER | SWP_SHOWWINDOW);

		// Set selection.
		::SetFocus (g_hLabelEditWnd);
		::SendMessage (g_hLabelEditWnd, EM_SETSEL, 0, rgn.strLabel.size ());
		::SendMessage (g_hLabelEditWnd, EM_SCROLLCARET, 0, 0);
	}
}

static
void _OnEditLabelComplete (bool _bDone = true) {
	if (! g_bDialogMode)
		return;
	g_bDialogMode = false;

	// Return keyboard input from the label edit box.
	::SetFocus (g_hWnd);

	::ShowWindow (g_hLabelEditWnd, SW_HIDE);

	if (g_Selection [0].pLayer && _bDone) {
		const size_t cLen = ::GetWindowTextLength (g_hLabelEditWnd) + 1;
		char * const strLabel = reinterpret_cast<char *> (::alloca (sizeof (char)*cLen));
		::GetWindowText (g_hLabelEditWnd, strLabel, cLen);

		if (g_Selection [0].iRgn->strLabel == strLabel)
			// NOTE: no changes.
			return;

		if (g_map.Header.CodeSchema == bpsAscii7 && ! Is6bitLabelCorrect (strLabel)) {
			ErrorMsgBox ("The label includes symbols not allowed for ASCII-7 coding (currently selected).");
			return;
		}

		RenameObject (* g_Selection [0].iRgn, strLabel);

		Refresh ();
	}
}

static
LRESULT APIENTRY _LabelEditProc (HWND _hWnd, UINT _uMsg, WPARAM _wParam, LPARAM _lParam) {
	switch (_uMsg) {
		case WM_KILLFOCUS:
			_OnEditLabelComplete ();
			break;

		case WM_CHAR:
			switch (_wParam) {
				case VK_RETURN: _OnEditLabelComplete (); return 0; // NOTE: don't call ::CallWindowProc() to avoid beep.
				case VK_ESCAPE: _OnEditLabelComplete (false); return 0;
			}
			break;
	}

	assert (g_wpLabelEdit);
	return ::CallWindowProc (g_wpLabelEdit, _hWnd, _uMsg, _wParam, _lParam);
}
